/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
 */

import { AddressInfo } from 'net';
import process from 'process';
import { Socket } from 'socket.io';

import { exit } from '../../boot/hooks/exit.js';
import { BuildEvent } from '../../common/daemon-protocol/build-event.js';
import { BuildStatus } from '../../common/daemon-protocol/common-enum.js';
import { createBuildStatus } from '../../common/daemon-protocol/message.js';
import { DAEMON_LISTEN_FAILED_REASON_PREFIX, DAEMON_SOCKETS_MAX_LIMIT } from '../const/daemon-const.js';
import { HvigorDaemonLogger } from '../log/hvigor-daemon-log.js';
import { getCurrentProcessState, setCurrentProcessState } from '../process/daemon-process-lifecycle.js';
import { DaemonState } from '../registry/daemon-info.js';
import { defaultDaemonInfoService } from '../registry/daemon-info-service.js';
import { defaultDaemonServerRegistry } from '../registry/daemon-server-registry.js';
import { defaultSessionManager } from '../session/session-manager.js';
import { DaemonServerSocketListenerRegistry } from '../socket/daemon-server-socket-listener-registry.js';
import { overridePrintWrite } from '../util/print-util.js';

import { HvigorDaemonServer } from './hvigor-daemon-server.js';

const _log = HvigorDaemonLogger.getLogger('daemon');

/**
 * 在daemon process中的server上需要添加的事件监听
 */
export class DaemonServerListenerRegistry {
  registryAllListener(hvigorDaemonServer: HvigorDaemonServer) {
    this.onListening(hvigorDaemonServer);
    this.onConnection(hvigorDaemonServer);
    this.onError(hvigorDaemonServer);
    this.onCloseConnection(hvigorDaemonServer);
    this.onListenFailed(hvigorDaemonServer);
    this.onCloseServer(hvigorDaemonServer);
  }

  /**
   * 当server建立起一个新的socket连接时触发，完成向会话中心注册socket等工作
   * @private
   */
  private onConnection(hvigorDaemonServer: HvigorDaemonServer) {
    hvigorDaemonServer.onConnection(async (socket) => {
      defaultSessionManager.initSession(socket);
      const socketsLength = defaultSessionManager.getLength();
      _log.debug(`hvigor daemon: Socket is connected. socketId=${socket.id}, connectionCount=${socketsLength}`);

      if (socketsLength > DAEMON_SOCKETS_MAX_LIMIT) {
        const reason =
          'hvigor daemon：The number of hvigor daemon connections has reached the upper limit, ' +
          'this socket will be disconnect. Please wait for one of the connections to be disconnected ' +
          'and then try again.';
        socket.emit(BuildEvent.BUILD_STATUS, createBuildStatus(BuildStatus.REJECT, -1, reason));
        socket.disconnect(true);
      }

      // 针对每一个链接的socket注册该socket需要跟client之间通信的事件监听
      // 注意此时还没有与主线程连接，需要区别哪些是必须的监听，哪些是仅需要在作为主线程输出socket所需要的监听
      const socketListenerRegistry = new DaemonServerSocketListenerRegistry(socket);
      socketListenerRegistry.registryBaseListener(hvigorDaemonServer);

      _log.debug(`hvigor daemon: daemonState=${getCurrentProcessState()} 
      daemonInfo=${JSON.stringify(defaultDaemonServerRegistry.getInfos())}`);

      // 当有链接建立,需要将process状态从IDLE状态或CANCELD转变为HAFL_BUSY状态
      if (getCurrentProcessState() === DaemonState.IDLE || getCurrentProcessState() === DaemonState.CANCELED) {
        setCurrentProcessState(DaemonState.HALF_BUSY);
      }
    });
  }

  /**
   * 当server侧主动断开连接时手动触发，完成会话清理等工作
   * @private
   */
  private onCloseConnection(hvigorDaemonServer: HvigorDaemonServer) {
    hvigorDaemonServer.onCloseConnection(async (socket: Socket) => {
      // 如果当前activeSocket是触发closeConnection的socket，就重置activeSocket
      if (socket.id === defaultSessionManager.getActiveSocket()?.id) {
        defaultSessionManager.resetActiveSocket();
      }
      defaultSessionManager.endSession(socket);

      const socketsLength = defaultSessionManager.getSockets().length;
      _log.debug(`hvigor daemon: Socket will be closed. socketId=${socket.id}, connectionCount=${socketsLength}`);

      // 当某个连接断开后,如果server侧的连接数已经变为0, 则需要将process状态从HALF_BUSY状态转变为IDLE状态,否则维持HALF_BUSY现状
      if (socketsLength === 0 && getCurrentProcessState() === DaemonState.HALF_BUSY) {
        setCurrentProcessState(DaemonState.IDLE);
      }
    });
  }

  private onCloseServer(hvigorDaemonServer: HvigorDaemonServer) {
    hvigorDaemonServer.onCloseServer((error) => {
      _log.debug(`hvigor daemon: connectionCount=${defaultSessionManager.getLength()}`);
      if (error) {
        _log.error(`hvigor daemon: ${error}`);
        if (error.stack) {
          _log.error(error.stack);
        }
      }

      [...defaultSessionManager.getSockets()].forEach((socket) => {
        socket.emit(BuildEvent.OUTPUT, error.stack);
        socket.emit(BuildStatus.CLOSE, createBuildStatus(BuildStatus.CLOSE, 0));
      });
    });
  }

  /**
   * server发生错误时执行的回调
   */
  private onError(hvigorDaemonServer: HvigorDaemonServer) {
    hvigorDaemonServer.onError((err: Error) => {
      _log.error(`Server error: ${err.message}`);
      exit(-1);
    });
  }

  /**
   * server开始监听端口后执行的回调方法
   * @private
   */
  private onListening(hvigorDaemonServer: HvigorDaemonServer) {
    hvigorDaemonServer.onListening(() => {
      const serverAddress = hvigorDaemonServer.getHttpServer().address() as AddressInfo;
      _log.debug(`hvigor daemon: listening connection. address=${serverAddress.address}, port=${serverAddress.port}.`);

      // daemonInfo由server侧生成
      const daemonInfo = defaultDaemonInfoService.createDefaultDaemonInfo(serverAddress, hvigorDaemonServer.getSessionId());

      // 此处必须在给client 发送消息之前调用,否则在server侧接受到新的connection的时候,会存在daemon info还未持久化的问题
      defaultDaemonServerRegistry.setInfo(daemonInfo.keyId, daemonInfo);

      // 当该回调调用时，server依然是client的子进程，因此可以直接将daemonInfo通过process.send方法传递
      if (process.send !== undefined) {
        process.send(JSON.stringify(daemonInfo));
      }

      // 开始将输出流都挂载到sessionManager上
      overridePrintWrite();
    });
  }

  /**
   * server监听端口失败时的回调
   * @private
   */
  private onListenFailed(hvigorDaemonServer: HvigorDaemonServer) {
    hvigorDaemonServer.onListenFailed((message: string) => {
      if (process.send !== undefined) {
        process.send(`${DAEMON_LISTEN_FAILED_REASON_PREFIX}${message}`);
      }
    });
  }
}
